﻿using SimpleESBClient.Handlers;
using SimpleESBCore.Common;
using SimpleESBCore.Implement;
using SimpleESBCore.Implement.Datas;
using SimpleESBCore.Implement.Events;
using SimpleESBCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SimpleESBClient
{
    public class EsbClient : IClient
    {
        ClientOption clientOption;
        ILog log;
        ISerialization serialization;
        Timer timerHeart;
        Socket clientHeartSocket;
        Socket clientDataSocket;
        SendHelper sh;
        Thread threadData;
        Thread threadHeart;
        Dictionary<string, IHandler<DataSourceBase>> dataHandler;
        public EsbClient(ClientOption clientOption, ILog log, ISerialization serialization)
        {
            this.clientOption = clientOption;
            this.log = log;
            this.serialization = serialization;
            this.sh = new SendHelper();
            this.dataHandler = new Dictionary<string, IHandler<DataSourceBase>>();
        }
        public void Connect()
        {
            try
            {
                //连接心跳服务
                EndPoint endPointHeart = new IPEndPoint(clientOption.ServerIPAddress, clientOption.HeartPort);
                clientHeartSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                clientHeartSocket.Connect(endPointHeart);
                log.LogInfo(new LogInfoData("连接心跳服务成功!"));
                //连接数据服务
                EndPoint endPointData = new IPEndPoint(clientOption.ServerIPAddress, clientOption.DataPort);
                clientDataSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                clientDataSocket.Connect(endPointData);
                log.LogInfo(new LogInfoData("连接数据服务成功!"));
                //开启心跳
                timerHeart = new Timer(new TimerCallback(HeartSpan), null, 0, Timeout.Infinite);
                //开始接收数据
                threadData = new Thread(ReceiveData);
                threadData.Start();
                threadHeart = new Thread(ReceiveHeart);
                threadHeart.Start();
                log.LogInfo(new LogInfoData("客户端开启成功!"));
            }
            catch (Exception ex)
            {
                log.LogInfo(new LogInfoData(ex.Message, LogType.Exception));
            }
        }

        private void HeartSpan(object obj)
        {
            try
            {
                timerHeart.Change(Timeout.Infinite, Timeout.Infinite);
                HeartEvent heartEvent = new HeartEvent();
                heartEvent.EventData = "ping";
                string message = serialization.Serializer(heartEvent);
                byte[] bufferResult = Encoding.Unicode.GetBytes(message);
                if (clientHeartSocket.Connected)
                {
                    clientHeartSocket.Send(bufferResult);
                }
            }
            catch(Exception ex)
            {
                clientHeartSocket.Close();
                clientHeartSocket = null;
                log.LogInfo(new LogInfoData(ex.Message));
            }
        }
        private void ReceiveData()
        {
            bool receiveLength = false;
            //此处用两个不同的缓冲区，是为了解决Tcp沾包的问题
            byte[] bufferLength = new byte[184];//实例化一个接收数据长度信息的缓冲区
            byte[] bufferSource = null;//实例化一个接收数据原始信息的缓冲区
            while (clientDataSocket.Connected)
            {
                try
                {
                    if (!receiveLength)
                    {
                        Array.Clear(bufferLength, 0, bufferLength.Length);
                        clientDataSocket.Receive(bufferLength);//接收数据并存放到缓冲区内
                        string str = Encoding.Unicode.GetString(bufferLength, 0, 184);//将信息转换成数据

                        DataSendEvent dataSendEvent = serialization.DeSerializer<DataSendEvent>(str);
                        if (dataSendEvent != null)
                        {
                            receiveLength = true;
                            var source = dataSendEvent.EventData as DataConfigData;
                            bufferSource = new byte[Convert.ToInt32(source.DataLength)];
                            continue;
                        }
                    }
                    else
                    {
                        Array.Clear(bufferSource, 0, bufferSource.Length);
                        receiveLength = false;
                        clientDataSocket.Receive(bufferSource);//接收数据并存放到缓冲区内
                        string str = Encoding.Unicode.GetString(bufferLength, 0, bufferSource.Length);//将信息转换成数据
                        if (!string.IsNullOrEmpty(str))
                        {
                            EventBase dataSendEvent = serialization.DeSerializer<EventBase>(str);
                            IHandler<EventBase> handler = new DataReceiveHandler(serialization, dataHandler, log);
                            handler.Handler(dataSendEvent);
                        }
                    }
                }
                catch (Exception ex)
                {
                    log.LogInfo(new LogInfoData(ex.Message));
                }
                Task.Delay(50);
            }
        }
        private void ReceiveHeart()
        {
            byte[] buffer = new byte[136];//实例化一个接收数据的缓冲区
            while (clientHeartSocket.Connected)
            {
                try
                {
                    Array.Clear(buffer, 0, 136);
                    clientHeartSocket.Receive(buffer);
                    string str = Encoding.Unicode.GetString(buffer, 0, 136);//将信息转换成数据
                    HeartEvent eventBase = serialization.DeSerializer<HeartEvent>(str);
                    IHandler<HeartEvent> handler = new HeartPongHandler();
                    handler.Handler(eventBase);

                    //刷新定时器时间,20S后重新发送一次数据
                    timerHeart.Change(20000, Timeout.Infinite);

                }
                catch (Exception ex)
                {
                    //log.LogInfo(new LogInfoData(ex.Message));
                    continue;
                }
                Thread.Sleep(50);
            }
        }

        public void CreateTopic(string topicName)
        {
            DataSendEvent dataSendEvent = new DataSendEvent();
            CreateTopicData createTopicData = new CreateTopicData(null, topicName);
            dataSendEvent.EventData = createTopicData;
            Publish(dataSendEvent);
        }

        public void DeleteTopic(string topicName)
        {
            DataSendEvent dataSendEvent = new DataSendEvent();
            DeleteTopicData deleteTopicData = new DeleteTopicData(null, topicName);
            dataSendEvent.EventData = deleteTopicData;
            Publish(dataSendEvent);
        }

        public void Publish(EventBase eventBase)
        {
            sh.SendData(clientDataSocket, eventBase, serialization);
        }

        public void RegisterHandler(IHandler<DataSourceBase> handler)
        {
            var typeName = handler.GetType().GetGenericArguments()[0].FullName;
            if (dataHandler.ContainsKey(typeName))
            {
                throw new Exception("你已注册过当前数据的事件处理程序!");
            }
            dataHandler.Add(typeName, handler);
        }
        public void DisConnect()
        {
            clientDataSocket.Close();
            clientHeartSocket.Close();
        }
    }
}
